home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************************
- *
- *
- * CursorUtilities.c - cursor stuff- spinning watch, etc
- *
- * 26/9/94 ©1994, Graham Cox
- *
- * This code based on "Macintosh Programming Secrets" 2nd Edition by Keith Rollin & Scott K
- *
- *************************************************************************************************/
-
- #include "CursorUtilities.h"
-
- #include <LowMem.h>
- #include <QDOffscreen.h>
-
- // globals:
-
- static short gVBLCount;
- static VBLTaskWithA5Ptr gCursorTask = NULL;
- static acurHdl gWatchSpinner = NULL;
- static acurHdl gBallSpinner = NULL;
- static acurHdl gBusyArrowSpinner = NULL;
- static CCrsrHandle gColourCursor = NULL;
- static Boolean gCursorPaused = FALSE;
- static long gPauseTicks;
- static Point gMouseTrack;
-
- short gLastCursorID = -1;
-
- // macro for doing dodgy 68000 stuff!
-
- #ifndef __powerc
- VBLTaskWithA5Ptr GetVBLRec(void) = 0x2008;
- #endif
-
- static Handle GetDetachedResource( ResType aType, short anID );
-
- /*-------------------------*** GETDETACHEDRESOURCE ***--------------------------------*/
- /*
-
- returns a detched resourse with type and ID passed.
-
- ----------------------------------------------------------------------------------------*/
-
- static Handle GetDetachedResource( ResType aType, short anID )
- {
- Handle temp = NULL;
-
- temp = GetResource( aType, anID );
-
- if ( temp )
- DetachResource( temp );
-
- return temp;
- }
-
- /*--------------------------*** INITANIMATEDCURSOR ***--------------------------------*/
- /*
-
- sets up an animated cursor by reading the 'acur' resource with the ID passed.
-
- ----------------------------------------------------------------------------------------*/
-
- acurHdl InitAnimatedCursor( short resID )
- {
- acurHdl temp;
- short ccount;
- CursHandle aCursor;
- CursHandle* workPtr;
-
- temp = (acurHdl) GetDetachedResource( 'acur', resID );
-
- if (temp)
- {
- HNoPurge((Handle) temp );
-
- ccount = (*temp)->numCursors;
- (*temp)->numCursors *= 32;
- (*temp)->index = 0;
-
- HLock((Handle) temp);
- workPtr = (*temp)->cursors;
-
- while( ccount-- )
- {
- aCursor = (CursHandle) GetDetachedResource( 'CURS', *(short*) workPtr );
- HNoPurge((Handle) aCursor );
- *workPtr++ = aCursor;
- }
- HUnlock((Handle) temp );
- }
-
- return temp;
- }
-
-
-
- /*-------------------------*** STARTCURSORANIMATION ***-------------------------------*/
- /*
- sets up the VBL queue to animate the cursor passed.
-
- ----------------------------------------------------------------------------------------*/
-
- void StartCursorAnimation( short period, acurHdl theCursor )
- {
- // primes the VBL queue to spin the cursor automatically
-
- VBLUPP vTaskUPP;
-
- if (theCursor)
- {
- LockCursorData(theCursor);
-
- gVBLCount = period;
- gCursorTask = (VBLTaskWithA5Ptr) NewPtr( sizeof( VBLTaskWithA5 ));
-
- gCursorTask->theTask.qType = vType;
-
- vTaskUPP = NewVBLProc((ProcPtr) VBLCursorSpin );
-
- gCursorTask->theTask.vblAddr = vTaskUPP;
- gCursorTask->theTask.vblCount = period;
- gCursorTask->theTask.vblPhase = 0;
- gCursorTask->A5 = (long) LMGetCurrentA5();
- gCursorTask->theCursor = theCursor;
-
- (void) VInstall((QElemPtr) gCursorTask );
-
- gCursorPaused = FALSE;
- }
- }
-
-
- /*-------------------------*** STOPCURSORANIMATION ***--------------------------------*/
- /*
-
- Kills the current cursor in the VBL queue and unlocks it. This then restores the arrow.
- ----------------------------------------------------------------------------------------*/
-
- void StopCursorAnimation()
- {
- // removes spinning cursor task from the VBL queue
-
- acurHdl theCursor;
-
- if ( gCursorTask )
- {
- (void) VRemove((QElemPtr) gCursorTask );
-
- theCursor = gCursorTask->theCursor;
- DisposeRoutineDescriptor( gCursorTask->theTask.vblAddr );
-
- DisposePtr((Ptr) gCursorTask);
- gCursorTask = NULL;
- UnlockCursorData( theCursor );
-
- // force switch to default cursor:
-
- gLastCursorID = -1;
- SetCursorShape( 0 );
- }
- }
-
-
- /*------------------------*** PAUSECURSORANIMATION ***--------------------------------*/
- /*
-
- Pauses the current animating cursor, returning the cursor to the shape passed. If the
- cursor is not animating, this does nothing. The animation can be restarted with a call
- to ResumeCursorAnimation().
- ----------------------------------------------------------------------------------------*/
-
- void PauseCursorAnimation( short tempFixedCursorID )
- {
- if ( CursorAnimating())
- {
- gCursorPaused = TRUE;
- gLastCursorID = -1;
- gPauseTicks = 0;
- }
- SetCursorShape( tempFixedCursorID );
- }
-
-
-
- /*------------------------*** RESUMECURSORANIMATION ***-------------------------------*/
- /*
- Restarts a paused animating cursor, if there is one, otherwise does nothing but reset the
- flag.
- ----------------------------------------------------------------------------------------*/
-
- void ResumeCursorAnimation()
- {
- gCursorPaused = FALSE;
- }
-
-
- /*-----------------------------*** ANIMATECURSOR ***----------------------------------*/
- /*
-
- Can be called repeatedly to animate a cursor. However, it is simpler to let the VBL task
- do this in general.
-
- ----------------------------------------------------------------------------------------*/
-
- void AnimateCursor( short increment, acurHdl theCursor )
- {
- // used only for non-VBL cursor spinning
-
- short oldIndex,newIndex;
- CursHandle aCursor;
-
- if ( theCursor && !gCursorPaused )
- {
- oldIndex = (*theCursor)->index / 32;
-
- (*theCursor)->index += increment;
- (*theCursor)->index %= (*theCursor)->numCursors;
-
- newIndex = (*theCursor)->index / 32;
-
- if (newIndex != oldIndex)
- {
- aCursor = (*theCursor)->cursors[newIndex];
- SetCursor(*aCursor);
- }
- }
- }
-
-
- /*----------------------------*** LOCKCURSORDATA ***----------------------------------*/
- /*
-
- locks down the cursor handles ready for animation
-
- ----------------------------------------------------------------------------------------*/
-
- void LockCursorData( acurHdl theCursor )
- {
- // locks the cursor and its internal data
-
- short ccount;
- CursHandle *workPtr;
-
- ccount = (*theCursor)->numCursors / 32;
-
- HLockHi((Handle) theCursor);
- workPtr = (*theCursor)->cursors;
-
- while (ccount--)
- HLockHi((Handle) *workPtr++);
- }
-
-
- /*--------------------------*** UNLOCKCURSORDATA ***----------------------------------*/
- /*
-
- unlocks the cursor handles
- ----------------------------------------------------------------------------------------*/
-
- void UnlockCursorData( acurHdl theCursor )
- {
- // unlocks the cursor
-
- short ccount;
- CursHandle *workPtr;
-
- ccount = (*theCursor)->numCursors / 32;
- workPtr = (*theCursor)->cursors;
-
- while (ccount--)
- HUnlock((Handle) *workPtr++);
-
- HUnlock((Handle) theCursor);
- }
-
- /*-----------------------------*** VBLCURSORSPIN ***----------------------------------*/
- /*
-
- this is the VBL callback that actually performs the animation of a VBL-driven cursor
- ----------------------------------------------------------------------------------------*/
-
- #ifndef __powerc
- void VBLCursorSpin()
- #else
- void VBLCursorSpin(VBLTaskWithA5Ptr theTask)
- #endif
- {
- // this is the function that is called from the interrupt task.
-
- acurHdl theCrsr;
- long oldA5;
-
- #ifndef __powerc
- VBLTaskWithA5Ptr theTask;
-
- theTask = GetVBLRec();
- oldA5 = SetA5(theTask->A5);
- if (LMGetCrsrBusy() == 0)
- {
- theCrsr = theTask->theCursor;
- AnimateCursor( 32, theCrsr );
- }
- theTask->theTask.vblCount = gVBLCount;
- (void) SetA5( oldA5 );
-
- #else
- oldA5 = SetA5( theTask->A5 );
-
- if (LMGetCrsrBusy() == 0)
- {
- theCrsr = theTask->theCursor;
- AnimateCursor( 32, theCrsr );
- }
- theTask->theTask.vblCount = gVBLCount;
- (void) SetA5( oldA5 );
- #endif
- }
-
-
- /*-----------------------------*** SETWATCHCURSOR ***---------------------------------*/
- /*
-
- starts the animated watch cursor. To stop it, call StopCursorAnimation.
- ----------------------------------------------------------------------------------------*/
-
- void SetWatchCursor()
- {
- // HL call installs VBL spinner for watch, which should have been inited beforehand
-
- if ( gCursorTask )
- StopCursorAnimation();
-
- if ( gWatchSpinner && ! gCursorTask )
- StartCursorAnimation( 8, gWatchSpinner );
- }
-
- /*---------------------------*** SETBEACHBALLCURSOR ***-------------------------------*/
- /*
-
- starts the animated "beachball" cursor. To stop it, call StopCursorAnimation.
- ----------------------------------------------------------------------------------------*/
-
- void SetBeachBallCursor()
- {
- if ( gCursorTask )
- StopCursorAnimation();
-
- if ( gBallSpinner && ! gCursorTask )
- StartCursorAnimation( 3, gBallSpinner );
-
- }
-
-
- void SetBusyArrowCursor()
- {
- if ( gCursorTask )
- StopCursorAnimation();
-
- if ( gBallSpinner && ! gCursorTask )
- StartCursorAnimation( 3, gBusyArrowSpinner );
- }
-
- /*-------------------------------*** GETMODIFIERS ***---------------------------------*/
- /*
-
- can be called at any time to get the current state of the keyboard modifier keys. Useful
- if the cursor is changed with different modifiers down.
- ----------------------------------------------------------------------------------------*/
-
- short GetModifiers()
- {
- // returns the current state of the modifier keys as if it was the modifier field in
- // an event record. This can be called at any time, even when an event is not available.
- // It is very useful for updating the cursor to show the modifier state.
-
- short modifiers = 0;
- unsigned char theKeys[16];
- KeyMap* pKeys;
-
- pKeys = (KeyMap*) theKeys;
-
- GetKeys( *pKeys );
-
- ((theKeys[0x37 >> 3] >> (0x37 & 7)) & 1)? modifiers |= cmdKey : 0;
- ((theKeys[0x38 >> 3] >> (0x38 & 7)) & 1)? modifiers |= shiftKey : 0;
- ((theKeys[0x39 >> 3] >> (0x39 & 7)) & 1)? modifiers |= alphaLock : 0;
- ((theKeys[0x3A >> 3] >> (0x3A & 7)) & 1)? modifiers |= optionKey : 0;
- ((theKeys[0x3B >> 3] >> (0x3B & 7)) & 1)? modifiers |= controlKey : 0;
-
- (! Button())? modifiers |= btnState : 0;
-
- return modifiers;
- }
-
-
- /*------------------------------*** APPCURSORINIT ***---------------------------------*/
- /*
- high-level call to initialise the two animated cursors. Extend this for others you may have
- ----------------------------------------------------------------------------------------*/
-
- void AppCursorInit()
- {
- // called once at application start-up to initialise the animated cursor globals.
-
- gWatchSpinner = InitAnimatedCursor( kWatchResID );
- gBallSpinner = InitAnimatedCursor( kBeachBallResID );
- gBusyArrowSpinner = InitAnimatedCursor( kBusyArrowResID );
-
- gLastCursorID = -1;
- gColourCursor = NULL;
- }
-
- /*------------------------------*** CURSORANIMATING ***-------------------------------*/
- /*
- returns TRUE if an animated cursor is on screen. You should not call
- SetCursor if this is true, or ugly cursor flickering occurs.
- ----------------------------------------------------------------------------------------*/
-
- Boolean CursorAnimating()
- {
- return (( gCursorTask != NULL ) && !gCursorPaused );
- }
-
-
-
- Boolean CursorAnimationVBLRunning()
- {
- return ( gCursorTask != NULL );
- }
-
-
- /*-------------------------------*** SETCURSORSHAPE ***-------------------------------*/
- /*
- You can use this instead of GetCursor/SetCursor to set the cursor shape. This also will
- correctly manage a colour cursor if there is one available with the same ID. This just
- works without any special coding, optimising for loading the colour cursor minimally.
- If you pass 0 it will set the arrow. This method is unsuitable for VBL animated cursors.
- ----------------------------------------------------------------------------------------*/
-
- void SetCursorShape( short resID )
- {
- if (( resID != gLastCursorID ) && ! CursorAnimating())
- {
- CCrsrHandle ncH = NULL;
-
- gLastCursorID = resID;
-
- if ( resID == 0 )
- SetCursor( &qd.arrow );
- else
- {
- // see if there's a colour cursor with that ID. We do not look for colour
- // cursors with IDs less than 128 since for some reason Inside Mac says
- // this is not allowed. Can't really see why this is!
-
- if ( resID > 127 )
- {
- ncH = GetCCursor( resID );
-
- if ( ncH )
- {
- // due to a subtle and undocumented side-effect of SetCCursor, we must be
- // careful to preserve the GDevice around this call, or bad things can
- // happen.
-
- GDHandle saveDevice;
-
- saveDevice = GetGDevice();
-
- SetCCursor( ncH );
- SetGDevice( saveDevice );
- }
- }
-
- // if no colour cursor, use a black and white one. This works with all
- // cursor IDs, including the standard iBeamCursor, crossCursor, etc.
-
- if ( ncH == NULL )
- {
- CursHandle cH;
-
- cH = GetCursor( resID );
-
- if ( cH )
- {
- HLockHi((Handle) cH );
- SetCursor( *cH );
- HUnlock((Handle) cH );
- }
- }
- }
-
- // dispose any old colour cursor
-
- if ( gColourCursor )
- DisposeCCursor( gColourCursor );
-
- // keep track of new one or NULL
-
- gColourCursor = ncH;
- }
- }
-